﻿using System;
using System.Text;
using System.Runtime.InteropServices;

namespace WiMo.Games.DirectDrawDisplay
{
    [Flags]
    public enum DMD
    {
        DMDO_0 = 0,
        DMDO_90 = 1,
        DMDO_180 = 2,
        DMDO_270 = 4,
    }

    public enum DM_Fields
    {
        DM_DISPLAYORIENTATION = 0x00800000,
        DM_DISPLAYQUERYORIENTATION = 0x01000000,
    }

    public enum DM_Orient
    {
        DMORIENT_PORTRAIT = 1,
        DMORIENT_LANDSCAPE = 2,
    }

    /* Flags for ChangeDisplaySettings */
    enum CDSFlags
    {
        CDS_TEST = 0x00000002,
        CDS_VIDEOPARAMETERS = 0x00000020,
        CDS_RESET = 0x40000000,
    }

    /* Return values for ChangeDisplaySettings */
    enum CDSRet
    {
        DISP_CHANGE_SUCCESSFUL = 0,
        DISP_CHANGE_RESTART = 1,
        DISP_CHANGE_FAILED = -1,
        DISP_CHANGE_BADMODE = -2,
        DISP_CHANGE_NOTUPDATED = -3,
        DISP_CHANGE_BADFLAGS = -4,
        DISP_CHANGE_BADPARAM = -5,
    }

    public class SelfMarshalledStruct
    {
        public SelfMarshalledStruct(int size)
        {
            data = new byte[size];
        }

        public Char GetChar(int offset)
        {
            return BitConverter.ToChar(data, offset);
        }

        public void SetChar(int offset, Char val)
        {
            Buffer.BlockCopy(BitConverter.GetBytes(val), 0, data, offset, 2);
        }

        public Int32 GetInt32(int offset)
        {
            return BitConverter.ToInt32(data, offset);
        }

        public void SetInt32(int offset, Int32 val)
        {
            Buffer.BlockCopy(BitConverter.GetBytes(val), 0, data, offset, 4);
        }

        public UInt32 GetUInt32(int offset)
        {
            return BitConverter.ToUInt32(data, offset);
        }

        public void SetUInt32(int offset, UInt32 val)
        {
            Buffer.BlockCopy(BitConverter.GetBytes(val), 0, data, offset, 4);
        }

        public Int16 GetInt16(int offset)
        {
            return BitConverter.ToInt16(data, offset);
        }

        public void SetInt16(int offset, Int16 val)
        {
            Buffer.BlockCopy(BitConverter.GetBytes(val), 0, data, offset, 2);
        }

        public UInt16 GetUInt16(int offset)
        {
            return BitConverter.ToUInt16(data, offset);
        }

        public void SetUInt16(int offset, UInt16 val)
        {
            Buffer.BlockCopy(BitConverter.GetBytes(val), 0, data, offset, 2);
        }

        public string GetStringUni(int offset, int len)
        {
            return Encoding.Unicode.GetString(data, offset, len).TrimEnd('\0');
        }

        public void SetStringUni(string str, int offset, int len)
        {
            Encoding.Unicode.GetBytes(str, 0, Math.Min(str.Length, len), data, offset);
        }

        public byte this[int offset]
        {
            get { return data[offset]; }
            set { data[offset] = value; }
        }

        public object Get(Type t, int offset)
        {
            if (t.IsPrimitive)
            {
                if (t.BaseType == typeof(Int32) || t == typeof(Int32))
                    return GetInt32(offset);
                else if (t.BaseType == typeof(Int16) || t == typeof(Int16))
                    return GetInt16(offset);
                else if (t.BaseType == typeof(UInt32) || t == typeof(UInt32))
                    return GetInt32(offset);
                else if (t.BaseType == typeof(UInt16) || t == typeof(UInt16))
                    return GetUInt16(offset);
                else if (t.BaseType == typeof(byte) || t == typeof(byte))
                    return this[offset];
            }
            return null;
        }

        public void Set(Type t, int offset, object Val)
        {
            if (t.IsPrimitive)
            {
                if (t.BaseType == typeof(Int32) || t == typeof(Int32))
                    SetInt32(offset, (int)Val);
                else if (t.BaseType == typeof(Int16) || t == typeof(Int16))
                    SetInt16(offset, (short)Val);
                else if (t.BaseType == typeof(UInt32) || t == typeof(UInt32))
                    SetUInt32(offset, (UInt32)Val);
                else if (t.BaseType == typeof(UInt16) || t == typeof(UInt16))
                    SetUInt16(offset, (ushort)Val);
                else if (t.BaseType == typeof(byte) || t == typeof(byte))
                    this[offset] = (byte)Val;
            }
            else if (t == typeof(string))
            {
                SetStringUni((string)Val, offset, ((string)Val).Length);
            }
        }

        protected byte[] data;
        public byte[] Data { get { return data; } }
    }

    public class devicemodeW : SelfMarshalledStruct
    {
        public devicemodeW()
            : base(192)
        {
            dmSize = (ushort)Data.Length;
        }
        public string dmDeviceName
        {
            get { return GetStringUni(0, 64); }


            set { SetStringUni(value, 0, 64); }
        }
        public ushort dmSpecVersion
        {
            get { return GetUInt16(64); }


            set { SetUInt16(64, value); }
        }
        public ushort dmDriverVersion
        {
            get { return GetUInt16(66); }


            set { SetUInt16(66, value); }
        }
        public ushort dmSize
        {
            get { return GetUInt16(68); }


            set { SetUInt16(68, value); }
        }
        public ushort dmDriverExtra
        {
            get { return GetUInt16(70); }


            set { SetUInt16(70, value); }
        }
        public DM_Fields dmFields
        {
            get { return (DM_Fields)GetUInt32(72); }


            set { SetUInt32(72, (uint)value); }
        }
        public DM_Orient dmOrientation
        {
            get { return (DM_Orient)GetInt16(76); }


            set { SetInt16(76, (short)value); }
        }
        public short dmPaperSize
        {
            get { return GetInt16(78); }


            set { SetInt16(78, value); }
        }
        public short dmPaperLength
        {
            get { return GetInt16(80); }


            set { SetInt16(80, value); }
        }
        public short dmPaperWidth
        {
            get { return GetInt16(82); }


            set { SetInt16(82, value); }
        }
        public short dmScale
        {
            get { return GetInt16(84); }


            set { SetInt16(84, value); }
        }
        public short dmCopies
        {
            get { return GetInt16(86); }


            set { SetInt16(86, value); }
        }
        public short dmDefaultSource
        {
            get { return GetInt16(88); }


            set { SetInt16(88, value); }
        }
        public short dmPrintQuality
        {
            get { return GetInt16(90); }


            set { SetInt16(90, value); }
        }
        public short dmColor
        {
            get { return GetInt16(92); }


            set { SetInt16(92, value); }
        }
        public short dmDuplex
        {
            get { return GetInt16(94); }


            set { SetInt16(94, value); }
        }
        public short dmYResolution
        {
            get { return GetInt16(96); }


            set { SetInt16(96, value); }
        }
        public short dmTTOption
        {
            get { return GetInt16(98); }


            set { SetInt16(98, value); }
        }
        public short dmCollate
        {
            get { return GetInt16(100); }


            set { SetInt16(100, value); }
        }
        public string dmFormName
        {
            get { return GetStringUni(102, 64); }


            set { SetStringUni(value, 102, 64); }
        }
        public ushort dmLogPixels
        {
            get { return GetUInt16(166); }


            set { SetUInt16(166, value); }
        }
        public uint dmBitsPerPel
        {
            get { return GetUInt32(168); }


            set { SetUInt32(168, value); }
        }
        public uint dmPelsWidth
        {
            get { return GetUInt32(172); }


            set { SetUInt32(172, value); }
        }
        public uint dmPelsHeight
        {
            get { return GetUInt32(176); }


            set { SetUInt32(176, value); }
        }
        public uint dmDisplayFlags
        {
            get { return GetUInt32(180); }


            set { SetUInt32(180, value); }
        }
        public uint dmDisplayFrequency
        {
            get { return GetUInt32(184); }


            set { SetUInt32(184, value); }
        }
        public DMD dmDisplayOrientation
        {
            get { return (DMD)GetUInt32(188); }


            set { SetUInt32(188, (uint)value); }
        }
    }

    public class ScreenMode
    {

        [DllImport("coredll.dll")]
        internal static extern CDSRet ChangeDisplaySettingsEx(
           string lpszDeviceName,
            /*DEVICEMODE*/ byte[] lpDevMode,
           IntPtr hwnd,
           CDSFlags dwflags,
           IntPtr lParam);

        public static string ChangeMode(int degree)
        {
            devicemodeW devMode = new devicemodeW();
            
            devMode.dmFields = DM_Fields.DM_DISPLAYORIENTATION;
            if (degree == 0)
                devMode.dmDisplayOrientation = DMD.DMDO_0;
            else if (degree == 90)
                devMode.dmDisplayOrientation = DMD.DMDO_90;
            else if (degree == 180)
                devMode.dmDisplayOrientation = DMD.DMDO_180;
            else if (degree == 270)
                devMode.dmDisplayOrientation = DMD.DMDO_270;

            CDSRet ret = ChangeDisplaySettingsEx(null, devMode.Data, IntPtr.Zero, 0, IntPtr.Zero);
            return (ret.ToString());

        }

    }
}
